package m.common.model.util;

import m.common.model.*;
import m.common.model.config.ModelConfig;
import m.common.model.type.FieldType;
import m.system.db.DBManager;
import m.system.exception.MException;
import m.system.util.*;

import java.sql.SQLException;
import java.util.*;

public class ModelUpdateUtil {
	/**
	 * 保存数据 oid为空则插入，oid不会空则更新
	 */
	public static <T extends Model> int saveModel(T obj) throws Exception {
		return saveModel(obj,null);
	}
	/**
	 * 保存数据 oid为空则插入，oid不会空则更新
	 * fields 只对更新有效
	 */
	public static <T extends Model> int saveModel(T obj,String... fields) throws Exception {
		if(ObjectUtil.isEmptyOid(obj)){
			obj.setOid(GenerateID.generatePrimaryKey());
			return insertModel(obj);
		}else{
			return updateModel(obj,fields);
		}
	}
	/**
	 * 全字段插入数据库. 空值不拼入sql, 数据库字段设置默认值有效
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> int insertModel(T obj) throws MException {
		// 获得对象的类型
		Class<T> clazz=(Class<T>) obj.getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException( clazz.getName()+"没有对应的表!");
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		StringBuilder fieldString=new StringBuilder();
		StringBuilder valueString=new StringBuilder();
		List<Object> valueParams= new ArrayList<>();
		List<Object> linkTableParams= new ArrayList<>();
		if(!tableMeta.oidIncrement()){//非自增添加主键
			if(StringUtil.isSpace(obj.getOid())){
				throw new MException("主键值为空!");
			}
			fieldString.append(tableMeta.oidField());
			valueString.append("?");
			valueParams.add(obj.getOid());
		}
		for(String key : fieldMap.keySet()){
			FieldMeta field=fieldMap.get(key);
			Object v=ClassUtil.getFieldValue(obj, key);
			if(null==v) continue;//插入时，空不插入
			fieldString.append(",").append(field.name());
			valueString.append(",?");
			if(ClassUtil.getDeclaredField(clazz,key).getType().isArray()){
				valueParams.add(ArrayUtil.connection((Object[]) v,","));
			}else if(v instanceof Date && (FieldType.INT==field.type() || FieldType.LONG==field.type())) {
				valueParams.add(((Date) v).getTime());
			}else{
				valueParams.add(v);
			}
		}
		if(tableMeta.oidIncrement()&&fieldString.length()>0){
			fieldString= new StringBuilder(fieldString.substring(1));
			valueString= new StringBuilder(valueString.substring(1));
		}
		Map<String,LinkTableMeta> imgMap= new HashMap<>();
		for(String key : linkTableMap.keySet()){
			LinkTableMeta linkTable=linkTableMap.get(key);
			if(linkTable.name().equals(tableMeta.oidField())) continue;
			Object bean=ClassUtil.getFieldValue(obj, key);
			if(null!=bean){
				fieldString.append(",").append(linkTable.name());
				valueString.append(",?");
				linkTableParams.add(ClassUtil.getFieldValue(bean, "oid"));
			}
		}
		StringBuilder sql=new StringBuilder("INSERT INTO ").append(tableMeta.name());
		sql.append("(").append(fieldString).append(") VALUES(").append(valueString).append(")");
		try {
			List<Object> paramList= new ArrayList<>();
			paramList.addAll(valueParams);
			paramList.addAll(linkTableParams);
			int n=0;
			if(tableMeta.oidIncrement()) {
				Integer id = DBManager.executeInsert(sql.toString(), paramList.toArray(new Object[]{}));
				if (null == id) throw new MException( "自增主键插入失败");
				obj.setOid(String.valueOf(id));
				n = 1;
			}else {
				n = DBManager.executeUpdate(sql.toString(), paramList.toArray(new Object[]{}));
			}
			return n;
		} catch (Exception e) {
			throw new MException("全量插入失败!"+e.getMessage());
		}
	}
	/**
	 * 全字段批量插入数据库  空值也拼入sql，数据库字段设置默认值无效，自增id列也无法使用
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> void insertModels(T... objs) throws MException {
		if(objs.length<1) return;
		// 获得对象的类型
		Class<T> clazz=(Class<T>) objs[0].getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		StringBuilder fieldString=new StringBuilder(tableMeta.oidField());
		StringBuilder valueString=new StringBuilder("?");
		List<List<Object>> valueParamsList= new ArrayList<>();
		List<List<Object>> linkTableParamsList= new ArrayList<>();
		Map<String,LinkTableMeta> imgMap= new HashMap<>();
		Date t=null;
		boolean isSysTime=false;
		//SysTimeModel模型自动填充sysUpdateTime
		if(clazz.isAssignableFrom(SysTimeModel.class)){
			t=new Date();
			isSysTime=true;
		}
		for(int i=0;i<objs.length;i++){
			if(isSysTime&&null==ClassUtil.getFieldValue(objs[i],"sysCreateTime"))
				ClassUtil.setFieldValue(objs[i],"sysCreateTime",t);
			if(isSysTime&&null==ClassUtil.getFieldValue(objs[i],"sysUpdateTime"))
				ClassUtil.setFieldValue(objs[i],"sysUpdateTime",t);
			List<Object> valueParams= new ArrayList<>();
			valueParams.add(objs[i].getOid());
			for(String key : fieldMap.keySet()){
				FieldMeta field=fieldMap.get(key);
				if(i==0){
					fieldString.append(",").append(field.name());
					valueString.append(",?");
				}
				Object v=ClassUtil.getFieldValue(objs[i], key);
				if(ClassUtil.getDeclaredField(clazz,key).getType().isArray()) {
					valueParams.add(ArrayUtil.connection((Object[]) v, ","));
				}else if(v instanceof Date && (FieldType.INT == field.type() || FieldType.LONG == field.type())) {
					valueParams.add(((Date) v).getTime());
				}else {
					valueParams.add(v);
				}
			}
			valueParamsList.add(valueParams);
			List<Object> linkTableParams= new ArrayList<>();
			for(String key : linkTableMap.keySet()){
				LinkTableMeta linkTable=linkTableMap.get(key);
				if(linkTable.name().equals(tableMeta.oidField())) continue;
				if(i==0){
					fieldString.append(",").append(linkTable.name());
					valueString.append(",?");
				}
				Object bean=ClassUtil.getFieldValue(objs[i], key);
				if(null==bean){
					linkTableParams.add(null);
				}else{
					linkTableParams.add(ClassUtil.getFieldValue(bean, "oid"));
				}
			}
			linkTableParamsList.add(linkTableParams);
		}
		StringBuilder sql=new StringBuilder("INSERT INTO ").append(tableMeta.name());
		sql.append("(").append(fieldString).append(") VALUES(").append(valueString).append(")");

		try {
			List<Object[]> paramList= new ArrayList<>();
			for(int i=0;i<valueParamsList.size();i++){
				valueParamsList.get(i).addAll(linkTableParamsList.get(i));
				paramList.add(valueParamsList.get(i).toArray(new Object[]{}));
			}
			DBManager.batchUpdate(sql.toString(), paramList);
		} catch (Exception e) {
			throw new MException("批量全插入失败!"+e.getMessage());
		}
	}

	/**
	 * 根据ModelQueryList查询插入clazz数据
	 * fields要与queryList查询出来的列一致,fields和queryList要有oid,queryList不能使用*
	 * 不会更新ImageLink关联数量，包含image的不推荐使用此方法
	 */
	public static <T extends Model> int insertQueryList(Class<T> clazz,String[] fields,ModelQueryList queryList) throws MException {
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz+"没有对应的表!");
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		StringBuilder fieldString=new StringBuilder();
		for(String key : fields){
			if("oid".equals(key)){
				fieldString.append(",oid");
				continue;
			}
			FieldMeta field=fieldMap.get(key);
			if(null!=field){
				fieldString.append(",").append(field.name());
				continue;
			}
			LinkTableMeta linkTable=linkTableMap.get(key);
			if(null!=linkTable) {
				fieldString.append(",").append(linkTable.name());
			}
		}
		if(fieldString.length()==0) throw new MException("没有要插入的字段");
		StringBuilder sql=new StringBuilder("INSERT INTO ").append(tableMeta.name());
		sql.append("(").append(fieldString.substring(1)).append(") ");

		try {
			QueryParameter qp=queryList.getQueryParameter();
			int num=DBManager.executeUpdate(sql.append(qp.getSql()).toString(), qp.getValueList().toArray(new Object[0]));
			return num;
		} catch (Exception e) {
			throw new MException("批量全插入失败!"+e.getMessage());
		}
	}
	/**
	 * 全字段更新记录.
	 */
	public static <T extends Model> int updateModel(T obj) throws Exception {
		return updateModel(obj,null);
	}
	/**
	 * 根据fields内容更新属性
	 *  obj 包含oid值
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> int updateModel(T obj,String... fields) throws MException {
		boolean flag= null != fields;
		if(StringUtil.isSpace(obj.getOid())){
			throw new MException("主键值为空!");
		}
		//SysTimeModel模型自动填充sysUpdateTime
		boolean isSysTime=false;
		if(obj instanceof SysTimeModel){
			isSysTime=true;
			ClassUtil.setFieldValue(obj,"sysUpdateTime",new Date());
		}
		// 获得对象的类型
		Class<T> clazz=(Class<T>) obj.getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		StringBuilder fieldString=new StringBuilder();
		String whereString = " WHERE "+tableMeta.oidField()+"=?";
		List<Object> valueParams= new ArrayList<>();
		List<Object> linkTableParams= new ArrayList<>();
		for(String key : fieldMap.keySet()){
			if(!flag||ArrayUtil.isContain(fields, key)
					||isSysTime&&key.equals("sysUpdateTime")){
				FieldMeta field=fieldMap.get(key);
				fieldString.append(",").append(field.name()).append("=?");
				Class<?> type=ClassUtil.getDeclaredField(clazz,key).getType();
				if(type.isArray()){
					valueParams.add(ArrayUtil.connection((Object[]) ClassUtil.getFieldValue(obj, key),","));
				}else {
					Object fv = ClassUtil.getFieldValue(obj, key);
					if(fv instanceof Date && (FieldType.INT == field.type() || FieldType.LONG == field.type())) {
						// 模型Date 数据库int
						valueParams.add(((Date)fv).getTime());
					}else {
						valueParams.add(ClassUtil.getFieldValue(obj, key));
					}
				}
			}
		}
		Map<String,LinkTableMeta> imgMap= new HashMap<>();
		for(String key : linkTableMap.keySet()){
			if(!flag||ArrayUtil.isContain(fields, key+".oid")||ArrayUtil.isContain(fields, key)){
				LinkTableMeta linkTable=linkTableMap.get(key);
				if(linkTable.name().equals("oid")) continue;
				fieldString.append(",").append(linkTable.name()).append("=?");
				Object bean=ClassUtil.getFieldValue(obj, key);
				if(null==bean){
					linkTableParams.add(null);
				}else{
					linkTableParams.add(ClassUtil.getFieldValue(bean, "oid"));
				}
			}
		}
		if(fieldString.length()<=0){
			throw new MException("没有更新的属性!");
		}
		StringBuilder sql=new StringBuilder("UPDATE ").append(tableMeta.name());
		sql.append(" SET ").append(fieldString.substring(1)).append(whereString);

		try {
			List<Object> paramList= new ArrayList<>();
			paramList.addAll(valueParams);
			paramList.addAll(linkTableParams);
			paramList.add(obj.getOid());
			int n=DBManager.executeUpdate(sql.toString(),paramList.toArray(new Object[]{}));
			return n;
		} catch (Exception e) {
			throw new MException("更新失败!"+e.getMessage());
		}
	}
	/**
	 * 批量更新 根据oid
	 *  objs
	 *  MException
	 */
	public static <T extends Model> void updateModels(T... objs) throws MException {
		updateModels(objs,null);
	}
	/**
	 * 全字段批量更新数据
	 *  <T>
	 *  objs
	 *  MException
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> void updateModels(T[] objs,String... fields) throws MException {
		boolean flag= null != fields;
		if(objs.length<1) return;
		// 获得对象的类型
		Class<T> clazz=(Class<T>) objs[0].getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		StringBuilder fieldString=new StringBuilder();
		String whereString = " WHERE "+tableMeta.oidField()+"=?";
		List<List<Object>> valueParamsList= new ArrayList<>();
		List<List<Object>> linkValueParamsList= new ArrayList<>();
		Map<String,LinkTableMeta> imgMap= new HashMap<>();
		Date t=null;
		boolean isSysTime=false;
		//SysTimeModel模型自动填充sysUpdateTime
		if(clazz.isAssignableFrom(SysTimeModel.class)){
			t=new Date();
			isSysTime=true;
		}
		for(int i=0;i<objs.length;i++){
			if(isSysTime&&null==ClassUtil.getFieldValue(objs[i],"sysUpdateTime"))
				ClassUtil.setFieldValue(objs[i],"sysUpdateTime",t);
			List<Object> valueParams= new ArrayList<>();
			for(String key : fieldMap.keySet()){
				if(!flag||ArrayUtil.isContain(fields, key)
						||isSysTime&&key.equals("sysUpdateTime")){
					FieldMeta field=fieldMap.get(key);
					if(i==0){
						fieldString.append(",").append(field.name()).append("=?");
					}
					Class<?> type=ClassUtil.getDeclaredField(clazz,key).getType();
					if(type.isArray()){
						valueParams.add(ArrayUtil.connection((Object[]) ClassUtil.getFieldValue(objs[i], key),","));
					}else {
						Object fv = ClassUtil.getFieldValue(objs[i], key);
						if(fv instanceof Date && (FieldType.INT == field.type() || FieldType.LONG == field.type())) {
							// 模型Date 数据库int
							valueParams.add(((Date)fv).getTime());
						}else {
							valueParams.add(ClassUtil.getFieldValue(objs[i], key));
						}
					}
				}
			}
			valueParamsList.add(valueParams);
			List<Object> linkValueParams= new ArrayList<>();
			for(String key : linkTableMap.keySet()){
				if(!flag||ArrayUtil.isContain(fields, key)||ArrayUtil.isContain(fields, key + ".oid")){
					LinkTableMeta field=linkTableMap.get(key);
					if(field.name().equals("oid")) continue;
					if(i==0){
						fieldString.append(",").append(field.name()).append("=?");
					}
					Object bean=ClassUtil.getFieldValue(objs[i], key);
					if(null==bean){
						linkValueParams.add(null);
					}else{
						linkValueParams.add(ClassUtil.getFieldValue(bean, "oid"));
					}
				}
			}
			linkValueParams.add(objs[i].getOid());
			linkValueParamsList.add(linkValueParams);
		}
		StringBuilder sql=new StringBuilder("UPDATE ").append(tableMeta.name());
		sql.append(" SET ").append(fieldString.substring(1)).append(whereString);

		try {
			List<Object[]> paramList= new ArrayList<>();
			for(int i=0;i<valueParamsList.size();i++){
				valueParamsList.get(i).addAll(linkValueParamsList.get(i));
				paramList.add(valueParamsList.get(i).toArray(new Object[]{}));
			}
			DBManager.batchUpdate(sql.toString(), paramList);
		} catch (Exception e) {
			throw new MException("批量全更新失败!"+e.getMessage());
		}
	}
	/**
	 * 删除数据,有oid属性即可
	 *  <T>
	 *  obj
	 *  MException
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> int deleteModel(T obj) throws MException {
		// 获得对象的类型
		Class<T> clazz=(Class<T>) obj.getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		String keyValue=obj.getOid();
		if(StringUtil.isSpace(keyValue)){
			throw new MException("主键值为空!");
		}
		StringBuilder sql=new StringBuilder("DELETE FROM ").append(tableMeta.name());
		sql.append(" WHERE ").append(tableMeta.oidField()).append("=?");
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		Map<String,LinkTableMeta> imgMap= new HashMap<>();
		for(String key : linkTableMap.keySet()){
			LinkTableMeta linkTable=linkTableMap.get(key);
		}
		try {
			int n=DBManager.executeUpdate(sql.toString(),keyValue);
			return n;
		} catch (Exception e) {
			throw new MException("删除操作失败!"+e.getMessage());
		}
	}
	/**
	 * 批量删除
	 *  <T>
	 *  objs
	 *  MException
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> void deleteModels(T... objs) throws MException {
		if(objs.length<1) return;
		// 获得对象的类型
		Class<T> clazz=(Class<T>) objs[0].getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		StringBuilder sql=new StringBuilder("DELETE FROM ").append(tableMeta.name());
		sql.append(" WHERE ").append(tableMeta.oidField()).append("=?");
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		Map<String,LinkTableMeta> imgMap= new HashMap<>();
		for(String key : linkTableMap.keySet()){
			LinkTableMeta linkTable=linkTableMap.get(key);
		}
		try {
			List<Object[]> paramList= new ArrayList<>();
			for(T obj : objs){
				paramList.add(new Object[]{obj.getOid()});
			}
			DBManager.batchUpdate(sql.toString(), paramList);
		} catch (Exception e) {
			throw new MException("批量删除操作失败!"+e.getMessage());
		}
	}
	/**
	 * 根据条件更新
	 *  obj
	 *  fields
	 *  condition  判断属性
	 *  返回更新条数
	 *  MException
	 *  SQLException
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Model> int update(T obj,String[] fields,QueryCondition condition) throws MException{
		// 获得对象的类型
		Class<T> clazz=(Class<T>) obj.getClass();
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		Map<String,FieldMeta> fieldMap=ModelConfig.getFieldMetaMap(clazz);
		Map<String,LinkTableMeta> linkTableMap=ModelConfig.getLinkTableMetaMap(clazz);
		StringBuilder fieldString=new StringBuilder();
		List<Object> valueParams= new ArrayList<>();
		List<Object> linkTableParams= new ArrayList<>();
		//SysTimeModel模型自动填充sysUpdateTime
		boolean isSysTime=false;
		if(obj instanceof SysTimeModel){
			isSysTime=true;
			ClassUtil.setFieldValue(obj,"sysUpdateTime",new Date());
		}
		for(String key : fieldMap.keySet()){
			if(ArrayUtil.isContain(fields, key)||isSysTime&&key.equals("sysUpdateTime")){
				FieldMeta field=fieldMap.get(key);
				fieldString.append(",").append(field.name()).append("=?");
				Class<?> type=ClassUtil.getDeclaredField(clazz,key).getType();
				if(type.isArray()){
					valueParams.add(ArrayUtil.connection((Object[]) ClassUtil.getFieldValue(obj, key),","));
				}else {
					Object fv = ClassUtil.getFieldValue(obj, key);
					if(fv instanceof Date && (FieldType.INT == field.type() || FieldType.LONG == field.type())) {
						// 模型Date 数据库int
						valueParams.add(((Date)fv).getTime());
					}else {
						valueParams.add(ClassUtil.getFieldValue(obj, key));
					}
				}
			}
		}
		for(String key : linkTableMap.keySet()){
			if(ArrayUtil.isContain(fields, key)||ArrayUtil.isContain(fields, key + ".oid")){
				LinkTableMeta linkTable=linkTableMap.get(key);
				if(linkTable.name().equals("oid")) continue;
				fieldString.append(",").append(linkTable.name()).append("=?");
				Object bean=ClassUtil.getFieldValue(obj, key);
				if(null==bean){
					linkTableParams.add(null);
				}else{
					linkTableParams.add(ClassUtil.getFieldValue(bean, "oid"));
				}
			}
		}
		if(fieldString.length()<=0){
			throw new MException("没有更新的属性!");
		}
		ModelQueryList queryList=ModelQueryList.instance(clazz, new String[]{"oid"}, null, condition);
		QueryParameter queryParam=queryList.getQueryParameter();
		StringBuilder sql=new StringBuilder("UPDATE ").append(tableMeta.name());
		sql.append(" SET ").append(fieldString.substring(1));
		sql.append(" WHERE ").append(tableMeta.oidField()).append(" in(select t0_oid from (");
		sql.append(queryParam.getSql());
		sql.append(") a)");
		try {
			List<Object> paramList= new ArrayList<>();
			paramList.addAll(valueParams);
			paramList.addAll(linkTableParams);
			paramList.addAll(queryParam.getValueList());
			int num=DBManager.executeUpdate(sql.toString(),paramList.toArray(new Object[]{}));
			return num;
		} catch (SQLException e) {
			throw new MException("更新属性失败!"+e.getMessage());
		}
	}
	/**
	 * 根据条件删除
	 *  clazz
	 *  condition  判断属性
	 *  返回更新条数
	 *  MException
	 *  SQLException
	 */
	public static <T extends Model> int delete(Class<T> clazz,QueryCondition condition) throws MException{
		TableMeta tableMeta=ModelConfig.getTableMeta(clazz);
		if(null==tableMeta) throw new MException(clazz.getName()+"没有对应的表!");
		ModelQueryList queryList=ModelQueryList.instance(clazz, new String[]{"oid"}, null, condition);
		QueryParameter queryParam=queryList.getQueryParameter();
		StringBuilder sql=new StringBuilder("DELETE FROM ").append(tableMeta.name());
		sql.append(" WHERE ").append(tableMeta.oidField()).append(" in(select t0_oid from (");
		sql.append(queryParam.getSql());
		sql.append(") a)");
		try {
			return DBManager.executeUpdate(sql.toString(),queryParam.getValueList().toArray(new Object[]{}));
		} catch (SQLException e) {
			throw new MException("删除失败!"+e.getMessage());
		}
	}
}
